package at.ltasat.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public final class IO {
	private static final byte BOOLEAN_TRUE = 0x0F, BOOLEAN_FALSE = 0x00;

	public static void writeString(final OutputStream out, final String value) throws IOException {
		writeByteArray(out, value.getBytes());
	}

	public static String readString(final InputStream in) throws IOException {
		return new String(readByteArray(in));
	}

	public static void writeByteArray(final OutputStream out, final byte[] value) throws IOException {
		writeInteger(out, value.length);
		for (int i = 0; i < value.length; i++) {
			writeByte(out, value[i]);
		}
	}

	public static byte[] readByteArray(final InputStream in) throws IOException {
		final byte[] value = new byte[readInteger(in)];
		for (int i = 0; i < value.length; i++) {
			value[i] = readByte(in);
		}
		return value;
	}

	public static void writeDouble(final OutputStream out, final double value) throws IOException {
		writeLong(out, Double.doubleToLongBits(value));
	}

	public static double readDouble(final InputStream in) throws IOException {
		return Double.longBitsToDouble(readLong(in));
	}

	public static void writeLong(final OutputStream out, final long value) throws IOException {
		writeInteger(out, (int) (value >>> 32));
		writeInteger(out, (int) (value >>> 00));
	}

	public static long readLong(final InputStream in) throws IOException {
		long value = 0;
		value |= (((long) readInteger(in)) << 32) & 0xFFFFFFFF00000000L;
		value |= (((long) readInteger(in)) << 00) & 0x00000000FFFFFFFFL;
		return value;
	}

	public static void writeInteger(final OutputStream out, final int value) throws IOException {
		writeShort(out, (short) (value >>> 16));
		writeShort(out, (short) (value >>> 0));
	}

	public static int readInteger(final InputStream in) throws IOException {
		int value = 0;
		value |= (((int) readShort(in)) << 16) & 0xFFFF0000;
		value |= (((int) readShort(in)) << 00) & 0x0000FFFF;
		return value;
	}

	public static void writeShort(final OutputStream out, final short value) throws IOException {
		writeByte(out, (byte) (value >>> 8));
		writeByte(out, (byte) (value >>> 0));
	}

	public static short readShort(final InputStream in) throws IOException {
		short value = 0;
		value |= (((short) readByte(in)) << 8) & 0xFF00;
		value |= (((short) readByte(in)) << 0) & 0x00FF;
		return value;
	}

	public static void writeByte(final OutputStream out, final byte value) throws IOException {
		log(value);
		out.write(value);
	}

	public static byte readByte(final InputStream in) throws IOException {
		byte value = (byte) in.read();
		log(value);
		return value;
	}

	public static void writeBoolean(final OutputStream out, final boolean value) throws IOException {
		writeByte(out, value ? BOOLEAN_TRUE : BOOLEAN_FALSE);
	}

	public static boolean readBoolean(final InputStream in) throws IOException {
		byte byteValue = readByte(in);
		boolean value;
		if (byteValue == BOOLEAN_TRUE) {
			value = true;
		} else if (byteValue == BOOLEAN_FALSE) {
			value = false;
		} else {
			throw new IOException("Can't read boolean, illegal representation: " + byteValue);
		}
		return value;
	}

	@SuppressWarnings("unused")
	private static void log(byte value) {
		if (false) {
			String str = Integer.toHexString(value);
			switch (str.length()) {
			case 0:
				str = "00";
				break;
			case 1:
				str = "0" + str;
				break;
			default:
				str = str.substring(str.length() - 2);
				break;
			}
			assert str.length() == 2;
			System.out.print(str);
			System.out.print(' ');
		}
	}

	private IO() {
		// nothing to do
	}
}
